package hyl.core.safe;

import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import hyl.core.MyFun;
import hyl.core.fun.MyByte;


/**
 * 对象化的对称式加密类 目的是为了用一个密钥连续加密数据,不用频繁实例化
 * <p>
 * 
 * 对称加密（加密解密密钥相同）：DES、DES3、AES
 * </p>
 * <p>
 * 安全性：DES<DES3=AES<br>
 * 
 * DES是一种分组密码算法，分组长度为64比特，密钥长度为56比特。<br>
 * AES数据分组长度为128比特，密钥长度为128/192/256比特。<br>
 * </p>
 * 
 * @author: zoudyaou qq:37798955
 * @date: 2018年9月29日 下午3:51:23
 * @copyright: 2018 zoudayou Inc. All rights reserved.
 *             注意：代码版权归邹达有所有，任何人未经授权不得私自用于商业目的
 */
public class MyDes extends MySecret{
	//private static final Logger Log = LoggerFactory.getLogger(MyDes.class);
	public static final String DES = "DES";
	public static final String AES = "AES";
	public static final String DESede = "DESede";
	public enum BC {
		CBC, ECB, CFB, OFB, NONE, PKCS5, PKCS7, PKCS8, PKCS12, OAEPWithSHA, No
	};



	protected BC _format = BC.NONE;
	protected BC _padding = BC.No;

	//protected static final String UTF8 = "UTF8";
	protected static final String PADDING = "Padding";
	protected static final byte[] _seed = "cnnthylsecretkey".getBytes();
	/**
	 * 参数按"算法/模式/填充模式" <br>
	 * (1)加密算法有：AES，DES，DESede(DES3)和RSA 四种<br>
	 * (2) 模式有CBC(有向量模式)和ECB(无向量模式)，向量模式可以简单理解为偏移量，<br>
	 * 使用CBC模式需要定义一个IvParameterSpec对象 <br>
	 * (3) 填充模式:<br>
	 * NoPadding: 加密内容不足8位用0补足8位, <br>
	 * Cipher类不提供补位功能，需自己实现代码给加密内容添加0, 如{65,65,65,0,0,0,0,0}<br>
	 * PKCS5Padding: 加密内容不足8位用余位数补足8位, <br>
	 * 如{65,65,65,5,5,5,5,5}或{97,97,97,97,97,97,2,2}; 刚好8位补8位8<br>
	 */
	protected String _bc = "AES/CBC/PKCS5Padding";
	protected Key _key;
	protected static Provider _provider;

	protected SecureRandom _random;
	/**
	 * 加密的密钥长度 
	 */
	protected int _keysize = 128;

////////////////////////////基础函数//////////////////////////

	/**
	 * AES加密函数 只适合单次使用 
	 * content: 加密内容 slatKey: 加密的盐，16位字符串 vectorKey: 加密的向量，16位字符串
	 */
	public static byte[] encrypt(byte[] content, String slatKey, String vectorKey) throws Exception {
		Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");// "AES/CBC/PKCS5Padding"
		SecretKey secretKey = new SecretKeySpec(slatKey.getBytes(), "AES");
		IvParameterSpec iv = new IvParameterSpec(vectorKey.getBytes());
		cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
		return cipher.doFinal(content);
	}

	/**
	 * AES解密函数 只适合单次使用 
	 * content: 解密内容(base64编码格式) slatKey: 加密时使用的盐，16位字符串 vectorKey: 加密时使用的向量，16位字符串
	 */
	public static byte[] decrypt(byte[] base64Content, String slatKey, String vectorKey) throws Exception {
		Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
		SecretKey secretKey = new SecretKeySpec(slatKey.getBytes(), "AES");
		IvParameterSpec iv = new IvParameterSpec(vectorKey.getBytes());
		cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
		return cipher.doFinal(base64Content);

	}

//	protected final IDoneBytes _idbs = new IDoneBytes() {
//		@Override
//		public byte[] get(byte[] temp) {
//			try {
//				return _cipher.doFinal(temp);
//			} catch (Exception e) {
//				return new byte[0];
//			}
//		}
//	};
	// 加密解密 公用 已经是最快的了

//	protected static Provider getProvider() {
//		if (_provider == null)
//			_provider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
//		return _provider;
//	};

	protected void set算法字符串(BC... ibc) {
		if (ibc.length > 0)
			_format = ibc[0];
		if (ibc.length > 1)
			_padding = ibc[1];
		_bc = _algorithm + '/' + _format + '/' + _padding + PADDING;
	}

	/**
	 * AES/CBC/NoPadding (128)<br>
	 * AES/CBC/PKCS5Padding (128) <br>
	 * AES/ECB/NoPadding (128)<br>
	 * AES/ECB/PKCS5Padding (128)<br>
	 * DES/CBC/NoPadding (56) <br>
	 * DESede/CBC/PKCS5Padding (168) <br>
	 * DESede/ECB/NoPadding (168) <br>
	 * DESede/ECB/PKCS5Padding (168)
	 */
	// 秘钥初始化需要的向量
	// 初始化向量参数，AES 为16bytes. DES 为8bytes.
	private static byte[] getIV(String algorithm) {
		if (DES.equals(algorithm))
			return "dkaisscc".getBytes();
		// else if (AES.equals(algorithm))
		return "ajidlfcjakcidjfl".getBytes();
	}

	// 规定 key 的长度
	private void iniKeySize(int... size) {
		if (size.length > 0 && (size[0] % 8 == 0)) {
			_keysize = size[0];
			return;
		}
		if (DES.equals(_algorithm))
			_keysize = 56;
		else
			_keysize = 128;
	}

	/**
	 * 创建随机数
	 */

	public void iniRand(byte[] seed) {
//		
		// 6F053D0776D9288CE1EFEC0D98634C5F
		try {
			_random = SecureRandom.getInstance("SHA1PRNG");
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		if (null == seed)
			_random.setSeed(_seed);
		else {
			_random.setSeed(seed);
		}
	}

	/**
	 * 创建强随机数
	 */
	public void iniRandStrong() {
		try {
			_random = SecureRandom.getInstanceStrong();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
	}
	
	////////////////////////////基础函数///////////////////////
	
	/**
	 * 默认是AES 加密
	 * @return
	 */
	public static MyDes getInstance() {
		return new MyDes(AES);
	}
	public static MyDes getInstance(String algorithm) {
		return new MyDes(algorithm);
	}

	public MyDes(String algorithm) {
		_algorithm = algorithm;
		set算法字符串(BC.CBC, BC.PKCS5);
		iniKeySize();

	}

	MyDes createKey() {
		return createKey(null);
	}

	public MyDes createKey(String 密码) {
		KeyGenerator kg;
		byte[] pass = null;
		if (!MyFun.isEmpty(密码)) {
			pass = 密码.getBytes();
			if (DES.equals(_algorithm)) {
				if (pass.length != 8) {
					pass = null;
					System.out.println("Des密码长度必须是8个字节");
				}
			} else if (AES.equals(_algorithm)) {
				if (!(pass.length == 16 || pass.length == 24 || pass.length == 32)) {
					pass = null;
					System.out.println("AES密码长度必须是16/24/32个字母或数字");
				}
			}
		}
		//System.out.println(ByteUtils.byteArrayToHexStr(pass));
		// size:{des:数据分组64bit, key长度64bit ;
		// aes:数据分组长度128bit ,密钥长度 128/192/256bit}
		try {
			kg = KeyGenerator.getInstance(_algorithm);
			iniRand(pass);// 生成随机的密钥
			kg.init(_keysize, _random);
			_key = kg.generateKey();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return this;

	}

	/**
	 * 
	 * @param mode 	Cipher.ENCRYPT_MODE 加密  	Cipher.DECRYPT_MODE 解密
	 * @param 密钥
	 * @return
	 * @throws Exception
	 */

	public MySecret getSecret(int mode) throws Exception {		
		_cipher = Cipher.getInstance(_algorithm);
		_cipher.init(mode, _key);
		return this;
	}
	public byte[] getKeyBytes() {
		return _key.getEncoded();
	}

	/**
	 * 获取加密后内容的十六进制字符串
	 * 
	 * @return
	 */
	public String getKeyHexString() {
		return MyByte.bytesToHex(_key.getEncoded());
	}

	/*
	 * 只创建des加密工具
	 * 
	 * @param pass
	 * 
	 * @return
	 * 
	 * @throws Exception
	 */
//	public MyDes iniDesKey(String pass) throws Exception {
//		DESKeySpec _keySpec = new DESKeySpec(pass.getBytes());
//		SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(_algorithm);
//		_key = keyFactory.generateSecret(_keySpec);
//		return this;
//	}
	/**
	 * cipher 是负责加密解密的核心对象
	 * 
	 * @param mode
	 * @return
	 * @throws Exception
	 */
	
	/**
	 * Cipher.ENCRYPT_MODE Cipher.DECRYPT_MODE
	 */
	public MySecret do1选择(int mode) throws Exception {
		if (_provider == null)
			_cipher = Cipher.getInstance(_bc);
		else
			_cipher = Cipher.getInstance(_bc, "BC");
		if (_format == BC.CBC) {
			IvParameterSpec ivSpec = new IvParameterSpec(getIV(_algorithm));
			_cipher.init(mode, _key, ivSpec);
		} else if (_random == null)
			_cipher.init(mode, _key);
		else
			_cipher.init(mode, _key, _random);
		return this;
	}

	// public Cipher getCipherIV(int mode) throws Exception {
	// IvParameterSpec ivSpec = new IvParameterSpec(getIV(_algorithm));
	// _cipher = Cipher.getInstance(_bc); // 实例化加密类，参数为加密方式，要写全
	// _cipher.init(mode, _secretKey, ivSpec);
	// return _cipher;
	// }
	// 52ns
	// public MyKey iniKey(int size) throws Exception {
	// iniRand(RT.RAND);
	// _provider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
	// Security.addProvider(_provider);
	// KeyGenerator kg = KeyGenerator.getInstance(_algorithm, "BC");
	// // KeyGenerator kg = KeyGenerator.getInstance(_algorithm);
	// // kg.init(256);
	// // kg.init(size);
	// kg.init(size);
	// _secretKey = kg.generateKey();
	// return this;
	// }
	// 469ns 最慢
	// public MyKey iniAesKey() {
	// _secretKey = new SecretKeySpec(_seed, _algorithm); // 两个参数，第一个为私钥字节数组，
	// 第二个为加密方式 AES或者DES
	// // Key key = keyFactory.generateSecret(keySpec);
	// return this;
	// }
	
}
